/*
 * $Header: $
 * $Id$
 *
 * Copyright (c) 2011, Michael DePhillips. All Rights Reserved.
 * This code may not be used without the express written permission
 * of the copyright holder, Michael DePhillips.
 */

package com.facebook.bannermaker.android.activity.makebanner;

import java.io.File;
import java.util.List;
import java.util.Vector;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;

import com.facbook.bannermaker.android.util.ActivityUtil;
import com.facebook.bannermaker.android.R;

/**
 * Activity for browsing inbox and acquiring tasks
 *
 * @author Michael DePhillips
 * 
 */
public class ScrollableImageView extends View {

	// Member Variables
    private Drawable m_cursorRect;  // rectangle to select where to make banner
    private float m_scaledCursorWidth;        // cursor width
    private float m_scaledCursorHeight;      // cursor height
    private int m_cursorX = 0;      // cursor upper left hand corner X coordinate
    private int m_cursorY = 0;      // cursor upper left hand corner y coordinate
    private int m_cursorOffsetX;    // offset x of users finger click
    private int m_cursorOffsetY;    // offset y of users finger click
    private boolean m_cursorFocus = false;  // true if the cursor has focus

    private Bitmap m_bmLargeImage; //bitmap large enough to be scrolled
    private float m_scaledBmpWidth;     // scaled bitmap width
    private float m_scaledBmpHeight;    // scaled bitmap height
    private Rect m_displayRect = null; //rect we display to
    private Rect m_scrollRect = null; //rect we scroll over our bitmap with
    private int m_scrollRectX = 0; //current left location of scroll rect
    private int m_scrollRectY = 0; //current top location of scroll rect
    private float m_scrollByX = 0; //x amount to scroll by
    private float m_scrollByY = 0; //y amount to scroll by
    private float m_startX = 1; //track x from one ACTION_MOVE to the next
    private float m_startY = 1; //track y from one ACTION_MOVE to the next

    // imageview variables
    private boolean m_isImageSet = false;
    private boolean m_viewSet = false;

    // the smallest the cursor can be (and intervals)
    private static final float SMALLEST_CURSOR_WIDTH = 245f;
    private static final float SMALLEST_CURSOR_HEIGHT = 33f;
    // size of 1 of the 5 banner images that it must be to look
    // good on the facebook banner
    private static final int BANNER_IMAGE_WIDTH = 98;
    private static final int BANNER_IMAGE_HEIGHT = 66;

    // motion event constants
    private static final int ZOOM = 1;
    private static final int DRAG = 2;
    private int mode = 0;
    private float oldDist = 0;
    PointF mid = new PointF();

    // Physical display width and height.
    private int m_viewWidth = 0;
    private int m_viewHeight = 0;
    private float m_conversionFactor = 1;
    private String m_filePath = null;

    private MakeBannerMemActivity m_makeBannerActivity;

    /**
     * Default constructor
     * @param context
     */
    public ScrollableImageView(Context context) {
        super(context);
        initializeImageRects();
    }

    /**
     * Default XML constructor
     * @param context
     * @param attrSet
     */
    public ScrollableImageView(Context context, AttributeSet attrSet) {
        super(context, attrSet);
        m_viewWidth = this.getWidth();
        m_viewHeight = this.getHeight();
        initializeImageRects();
    }

    // Our view dimensions change depending on, among other things, screen
    // orientation. onSizeChanged() is a notification that such a change has 
    // occurred. For our purposes, we can use the newly changed values to set up
    // our scroll and display rectangles. This is how we handle a user switch
    // between portrait and landscape modes, or any other type of 'view has 
    // changed' operation.
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        // make sure setupBitmap has been called before this method does anything
        if( m_makeBannerActivity != null && !m_makeBannerActivity.isPostingBanner() ) {
            // view is configured
            m_viewSet = true;

            // Cache our new dimensions; we'll need them for drawing.
            m_viewWidth = w;
            m_viewHeight = h;

            if( m_isImageSet ) {    
                //*** this isn't necassarily right.  how i had it before was right
                // it should not do this everytime.  only if image isnt set.
                setupBitmap();   
            }
            initializeImageRects();
        }

        super.onSizeChanged(w, h, oldw, oldh);
    }

    // Adjusts the zoom level and performs the necassary conversions
    public void adjustZoomLevel(float factor) {

        float tempConversionFactor = m_conversionFactor + factor;       

        // zoom in with cursor and image
        if( tempConversionFactor > 0.5 ) {
            // make sure we are not zooming in too much (half screen), its pointless.
            if(((float)m_bmLargeImage.getWidth()/(float)tempConversionFactor)>(this.getWidth()/2)) {
                // make sure we are not zooming in too much (half screen), its pointless.
                if(((float)m_bmLargeImage.getHeight()/(float)tempConversionFactor)>(this.getHeight()/2)) {

                    // zoom in or out
                    m_conversionFactor += factor;   
                    // adjust bitmap width and height
                    m_scaledBmpWidth = (float)m_bmLargeImage.getWidth()/m_conversionFactor;
                    m_scaledBmpHeight = (float)m_bmLargeImage.getHeight()/m_conversionFactor;

                    // shrink the width of the view if the image is small
                    m_viewWidth = this.getWidth();
                    m_viewHeight = this.getHeight();
                    if( m_scaledBmpWidth < m_viewWidth ) {
                        // Destination rect for our main canvas draw.
                        int height = m_displayRect.bottom;
                        m_displayRect = new Rect(0, 0, (int)m_scaledBmpWidth, height);
                        m_viewWidth = (int)(m_bmLargeImage.getWidth()/m_conversionFactor);
                    }
                    else {
                        int height = m_displayRect.bottom;
                        m_displayRect = new Rect(0, 0, m_viewWidth, height);
                    }
                    if( m_scaledBmpHeight < m_viewHeight ) {
                        // Destination rect for our main canvas draw.
                        int width = m_displayRect.right;
                        m_displayRect = new Rect(0, 0, width, (int)m_scaledBmpHeight);  
                        m_viewHeight = (int)(m_bmLargeImage.getHeight()/m_conversionFactor);
                    }     
                    else {
                        int width = m_displayRect.right;
                        m_displayRect = new Rect(0, 0, width, m_viewHeight);  
                    }

                    // adjust size of cursor
                    m_cursorX *= ((float)(m_conversionFactor-factor)/(float)m_conversionFactor);
                    m_cursorY *= ((float)(m_conversionFactor-factor)/(float)m_conversionFactor);
                    m_scaledCursorWidth *= ((float)(m_conversionFactor-factor)/(float)m_conversionFactor);
                    m_scaledCursorHeight *= ((float)(m_conversionFactor-factor)/(float)m_conversionFactor);

                    // redraw changes
                    invalidate();
                }
            }
        }
    }

    /**
     * Inner class method to reset the scrollable rect area
     */
    private void initializeImageRects() {
        // Destination rect for our main canvas draw.
        m_displayRect = new Rect(0, 0, m_viewWidth, m_viewHeight);
        // Scroll rect: this will be used to 'scroll around' over the
        // bitmap in memory. Initialize as above.
        m_scrollRect = new Rect(0, 0, m_viewWidth, m_viewHeight);
        m_scrollRectX = 0; //current left location of scroll rect
        m_scrollRectY = 0; //current top location of scroll rect
    }

    /**
     * Generate all valid cursor dimensions from smallest aspect ratio of 98 x 66
     * to largest aspect ratio to fit within picture
     */
    private void generateCursorDimensions() {

        m_cursorX = 0;      // cursor upper left hand corner X coordinate
        m_cursorY = 0;      // cursor upper left hand corner y coordinate

        // find the largest the cursor can be but still be over the image
        int limitingFactor = (int)(m_bmLargeImage.getWidth()/SMALLEST_CURSOR_WIDTH);            
        int cursorDimensionsWithHeight = (int)(m_bmLargeImage.getHeight()/SMALLEST_CURSOR_HEIGHT);          
        if( limitingFactor > cursorDimensionsWithHeight ) {
            limitingFactor = cursorDimensionsWithHeight;
        }        

        // set the cursor to 490x66 or 250x33 and scale it
        if( limitingFactor == 1 ) {
            m_scaledCursorWidth = SMALLEST_CURSOR_WIDTH;
            m_scaledCursorHeight = SMALLEST_CURSOR_HEIGHT;
        }
        else {
            m_scaledCursorWidth = SMALLEST_CURSOR_WIDTH*2;
            m_scaledCursorHeight = SMALLEST_CURSOR_HEIGHT*2;
        }
        m_scaledCursorWidth /= m_conversionFactor;
        m_scaledCursorHeight /= m_conversionFactor;

        // set cursor rect dimensions and create it          
        m_cursorRect = getResources().getDrawable(R.drawable.rect);
        m_cursorRect.setBounds(m_cursorX, m_cursorY,(int)m_scaledCursorWidth + m_cursorX, (int)m_scaledCursorHeight + m_cursorY);
    }

    /**
     * Returns the cursor's current width after scaling
     * @return
     */
    public int getCursorWidth() {
        return Math.round(m_scaledCursorWidth*m_conversionFactor);
    }

    /**
     * Returns the cursor's current height after scaling
     * @return
     */
    public int getCursorHeight() {
        return Math.round(m_scaledCursorHeight*m_conversionFactor);
    }

    /**
     * Decrease cursor size if possible to
     */
    public void decreaseCursorSize() {

        // check to see if the cursor width is larger than 245, because that's the last time you can decrease it
        if( (m_scaledCursorWidth*m_conversionFactor) > SMALLEST_CURSOR_WIDTH) {
            m_scaledCursorWidth -= (SMALLEST_CURSOR_WIDTH/m_conversionFactor);
            m_scaledCursorHeight -= (SMALLEST_CURSOR_HEIGHT/m_conversionFactor);
            m_makeBannerActivity.updateCursorSizeValues(this);
        }
    }

    /**
     * Increase cursor size if possible to
     */
    public void increaseCursorSize() {

        // check to see if adding more to the cursor will go past pounds
        if( m_scaledCursorWidth+(SMALLEST_CURSOR_WIDTH/m_conversionFactor) < m_scaledBmpWidth ) {
            if( m_scaledCursorHeight+(SMALLEST_CURSOR_HEIGHT/m_conversionFactor) < m_scaledBmpHeight ) {
                m_scaledCursorWidth += (SMALLEST_CURSOR_WIDTH/m_conversionFactor);
                m_scaledCursorHeight += (SMALLEST_CURSOR_HEIGHT/m_conversionFactor); 

                // check to see if the x and y of the cursor will push it past the image, adjust if necassary
                if( m_cursorX+m_scaledCursorWidth > m_scaledBmpWidth ) {
                    m_cursorX = (int)(m_scaledBmpWidth-m_scaledCursorWidth);
                }
                if( m_cursorY+m_scaledCursorHeight > m_scaledBmpHeight ) {
                    m_cursorY = (int)(m_scaledBmpHeight-m_scaledCursorHeight);
                }
            }
            m_makeBannerActivity.updateCursorSizeValues(this);
        }    
    }

    private void setupBitmap() {

        int bmpWidth = m_bmLargeImage.getWidth();
        int bmpHeight = m_bmLargeImage.getHeight(); 

        // downsample the image to fit the screen nicely
        // but, keep the fullsize image in memory for exporting
        int limitingConversionWidth = bmpWidth / m_viewWidth;
        int limitingConversionHeight = bmpHeight / m_viewHeight;

        // if either of them are zero, then don't scale and make 1
        if( limitingConversionWidth == 0 ) {
            limitingConversionWidth++;
        }
        if( limitingConversionHeight == 0 ) {
            limitingConversionHeight++;
        }

        // scale only if the limiting factor is more than 1
        int limitingConversionFactor = Math.min(limitingConversionWidth, limitingConversionHeight);
        if( limitingConversionFactor > 0 ) {
            m_conversionFactor = limitingConversionFactor;     
            m_scaledBmpWidth = bmpWidth/m_conversionFactor;
            m_scaledBmpHeight = bmpHeight/m_conversionFactor;

            // scale the view down to the image size to avoid shaking
            if( m_scaledBmpWidth < m_viewWidth ) {
                m_viewWidth = (int)m_scaledBmpWidth;            
            }
            if( m_scaledBmpHeight < m_viewHeight ) {
                m_viewHeight = (int)m_scaledBmpHeight;            
            }  
        }   

        generateCursorDimensions();        
    }

    /**
     * Sets the bitmap to a new picture
     * @param bmpNew
     */
    public boolean setBitmap(MakeBannerMemActivity activity, String filePath) {

        boolean success = true;

        // if the file is > 4 MB in size, then scale it to save phone memory
        File file = new File(filePath);
        long fileLength = file.length();
        final long FOUR_MB = 4000000;
        int tempConversionFactor = (int)(fileLength / FOUR_MB) + 1;    

        // here w and h are the desired width and height        
        // bitmap is the resized bitmap
        if( tempConversionFactor > 1 ) {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = (int)tempConversionFactor;
            m_bmLargeImage = BitmapFactory.decodeFile(filePath, options);
        }
        else {
            m_bmLargeImage = BitmapFactory.decodeFile(filePath);
        }

        if( m_bmLargeImage == null ) {
            // new file was not a valid bitmap, try reloading last image if possible
            if( m_filePath != null ) {
                file = new File(m_filePath);
                fileLength = file.length();
                tempConversionFactor = (int)(fileLength / FOUR_MB) + 1;    

                if( tempConversionFactor > 1 ) {
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inSampleSize = (int)tempConversionFactor;
                    m_bmLargeImage = BitmapFactory.decodeFile(m_filePath, options);
                }
                else {
                    m_bmLargeImage = BitmapFactory.decodeFile(m_filePath);
                }
                m_isImageSet = true;
            }
            // could not set bitmap
            return false;
        }


        // after we know the file is a valid bitmap, change the settings
        m_conversionFactor = tempConversionFactor;
        m_makeBannerActivity = activity;
        m_filePath = filePath;

        int bmpWidth = m_bmLargeImage.getWidth();
        int bmpHeight = m_bmLargeImage.getHeight(); 

        // make sure the image is at least 98 by 66 pixels
        if(  bmpWidth < SMALLEST_CURSOR_WIDTH ) {
            AlertDialog alert = ActivityUtil.getAlertOkFinishDialogue(m_makeBannerActivity, "Width must be at least " + SMALLEST_CURSOR_WIDTH + " pixels");
            alert.show();
            return false;
        }
        else if( bmpHeight < SMALLEST_CURSOR_HEIGHT ) {
            AlertDialog alert = ActivityUtil.getAlertOkFinishDialogue(m_makeBannerActivity, "Height must be at least " + SMALLEST_CURSOR_HEIGHT + " pixels");
            alert.show();
            return false;
        }          

        if( success ) {

            // if view is configured, set up the bitmap according to view size
            if( m_viewSet ) {
                // shrink the width of the view if the image is small
                if( m_bmLargeImage.getWidth()/m_conversionFactor < m_viewWidth ) {
                    m_viewWidth = (int)(m_bmLargeImage.getWidth()/m_conversionFactor);            
                }
                if( m_bmLargeImage.getHeight()/m_conversionFactor < m_viewHeight ) {
                    m_viewHeight = (int)(m_bmLargeImage.getHeight()/m_conversionFactor);            
                }
                setupBitmap();
            }

            // initialize cursor attributes
            initializeImageRects();
            generateCursorDimensions();
            m_makeBannerActivity.updateCursorSizeValues(this);

            m_isImageSet = success;
            invalidate();
        }

        return true;
    }       

    /**
     * Clears bitmaps and frees up memory
     */
    public void recycleBitmaps() {
        if( m_isImageSet ) {
            m_bmLargeImage.recycle();
            System.gc();
            m_isImageSet = false;
        }
    }

    /**
     * Return if the image has been set
     * @return
     */
    public boolean isImageSet() {
        return m_isImageSet;
    }


    /**
     * Creates five new cropped bitmaps from where the cursor is over the image
     * 
     * @return list of Bitmap objects
     */
    public List<Bitmap> exportToImages() {

        List<Bitmap> bitmaps = new Vector<Bitmap>();        
        Bitmap bmp = null;

        // rescale cursor width and height
        int cursorWidth = (int)(m_scaledCursorWidth * m_conversionFactor);
        int cursorHeight = (int)(m_scaledCursorHeight * m_conversionFactor);

        // get the size of the cursor split vertically in fives
        // scale each cursor dimensions back to full resolution
        int bmpWidth =  (int)(cursorWidth / 5);
        int bmpHeight = (int)cursorHeight;

        // calculate the scale to make each bitmap 98 by 66
        float scaleWidth = ((float) BANNER_IMAGE_WIDTH) / (((float)cursorWidth)/ 5.0f);
        float scaleHeight = ((float) BANNER_IMAGE_HEIGHT) / ((float)cursorHeight);

        // create a matrix for the manipulation
        Matrix matrix = new Matrix();
        // resize the bitmap
        matrix.postScale(scaleWidth, scaleHeight);

        // scale cursors starting points
        // cursor is already scaled correctly
        m_cursorX *= m_conversionFactor;
        m_cursorY *= m_conversionFactor;

        // BUG FIX
        // sometimes there would be an off by 1 pixel error on scaling odd dimension images
        // to fix that, simply check for boundaries and adjust when necassary
        if( (m_cursorX+bmpWidth*5) > m_bmLargeImage.getWidth()) {
            m_cursorX = m_bmLargeImage.getWidth() - bmpWidth*5;
        }
        if( (m_cursorY+bmpHeight) > m_bmLargeImage.getHeight()) {
            m_cursorY = m_bmLargeImage.getHeight() - bmpHeight;
        }

        // first in last out of scaled banner images
        for(int i = 0; i < 5; i++) {
            bmp = Bitmap.createBitmap(m_bmLargeImage, m_cursorX + i * bmpWidth, 
                    m_cursorY, bmpWidth, bmpHeight, matrix, true);
            bitmaps.add(0, bmp);
        }

        // free up memory
        recycleBitmaps();

        return bitmaps;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if( m_isImageSet ) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {

            case MotionEvent.ACTION_DOWN:
                // Remember our initial down event location.
                mode = DRAG;
                float x = event.getX();
                float y = event.getY();

                // the cursor's relative x and y if it in the screen
                // even though it could be at 1600pxs (x-coord) if the scroll rect is
                // at 1600pxs too, the cursor at 0px on the screen
                int relativeX = m_cursorX - (int)(m_scrollRectX/m_conversionFactor);
                int relativeY = m_cursorY - (int)(m_scrollRectY/m_conversionFactor);
                m_cursorFocus = false;

                // perform basic collision detection
                if( relativeX < x ) {
                    if( (relativeX + m_scaledCursorWidth) > x ) {
                        if( relativeY < y ) {
                            if( (relativeY + m_scaledCursorHeight) > y ) {
                                m_cursorFocus = true;
                                m_cursorOffsetX = (int)x - relativeX;
                                m_cursorOffsetY = (int)y - relativeY;
                                m_scrollByX = 0;
                                m_scrollByY = 0;
                            }
                        }  
                    }
                }
                if( !m_cursorFocus ) {
                    m_startX = event.getRawX();
                    m_startY = event.getRawY();
                }

                break;


            case MotionEvent.ACTION_POINTER_DOWN:

                m_cursorFocus = false;
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    midPoint(mid, event);
                    mode = ZOOM;
                }

                break;

            case MotionEvent.ACTION_MOVE:
                if( mode == DRAG ) {
                    if( !m_cursorFocus ) {
                        x = event.getRawX();
                        y = event.getRawY();
                        // Calculate move update. This will happen many times
                        // during the course of a single movement gesture.
                        m_scrollByX = x - m_startX; //move update x increment
                        m_scrollByY = y - m_startY; //move update y increment
                        m_startX = x; //reset initial values to latest
                        m_startY = y;
                    }
                    else {
                        int newCursorX = (int)event.getX() + (int)(m_scrollRectX/m_conversionFactor) - m_cursorOffsetX;
                        int newCursorY = (int)event.getY() + (int)(m_scrollRectY/m_conversionFactor) - m_cursorOffsetY;

                        m_cursorX = newCursorX;
                        m_cursorY = newCursorY;
                        // check boundary conditions
                        if( newCursorX < 0 ) {
                            m_cursorX = 0;
                        }              
                        else if( (newCursorX + m_scaledCursorWidth) > m_scaledBmpWidth ) {
                            m_cursorX = Math.round(m_scaledBmpWidth - m_scaledCursorWidth);
                        }
                        if( newCursorY < 0 ) {
                            m_cursorY = 0;
                        } 
                        else if( (newCursorY + m_scaledCursorHeight) > m_scaledBmpHeight ) {
                            m_cursorY = Math.round(m_scaledBmpHeight - m_scaledCursorHeight);
                        }
                    }
                    invalidate();  // force a redraw
                }
                else if ( mode == ZOOM ) {
                    float newDist = spacing(event);
                    if (newDist > 10f) {
                        float diff = newDist - oldDist;
                        if( diff > 50 ) {
                            // convert cursor dimensions
                            adjustZoomLevel(-.1f);
                            oldDist = newDist;
                            invalidate();
                        }
                        else if( diff < -50 ) {
                            // convert cursor dimensions
                            adjustZoomLevel(.1f);
                            oldDist = newDist;
                            invalidate();                           
                        }
                    }
                }
                break;               
            }
        }
        return true; //done with this event so consume it
    }

    /**
     * 
     * @param event
     * @return
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }

    /**
     * 
     * @param point
     * @param event
     */
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if( m_isImageSet ) {
            // Our move updates are calculated in ACTION_MOVE in the opposite direction
            // from how we want to move the scroll rect. Think of this as dragging to
            // the left being the same as sliding the scroll rect to the right.
            int newm_scrollRectX = m_scrollRectX - (int)(m_scrollByX*m_conversionFactor);
            int newm_scrollRectY = m_scrollRectY - (int)(m_scrollByY*m_conversionFactor);

            // Don't scroll off the left or right edges of the bitmap.
            if (newm_scrollRectX < 0)
                newm_scrollRectX = 0;
            else if (newm_scrollRectX > (m_bmLargeImage.getWidth() - m_viewWidth*m_conversionFactor))
                newm_scrollRectX = (int)(m_bmLargeImage.getWidth() - m_viewWidth*m_conversionFactor);

            // Don't scroll off the top or bottom edges of the bitmap.
            if (newm_scrollRectY < 0)
                newm_scrollRectY = 0;
            else if (newm_scrollRectY > (m_bmLargeImage.getHeight() - m_viewHeight*m_conversionFactor))
                newm_scrollRectY = (int)(m_bmLargeImage.getHeight() - m_viewHeight*m_conversionFactor);

            // We have our updated scroll rect coordinates, set them and draw.
            m_scrollRect.set(newm_scrollRectX, newm_scrollRectY, 
                    newm_scrollRectX + (int)(m_viewWidth*m_conversionFactor), newm_scrollRectY + (int)(m_viewHeight*m_conversionFactor));
            Paint paint = new Paint();
            paint.setAntiAlias(false);
            paint.setFilterBitmap(true);
            canvas.drawBitmap(m_bmLargeImage, m_scrollRect, m_displayRect, paint);        

            // Reset current scroll coordinates to reflect the latest updates, 
            // so we can repeat this update process.
            // get relative cursor and draw rectangle
            m_scrollRectX = newm_scrollRectX;
            m_scrollRectY = newm_scrollRectY;

            int relativeX = m_cursorX - (int)(m_scrollRectX/m_conversionFactor);
            int relativeY = m_cursorY - (int)(m_scrollRectY/m_conversionFactor);

            m_cursorRect.setBounds(relativeX, relativeY, 
                    relativeX + (int)(m_scaledCursorWidth), 
                    relativeY + (int)(m_scaledCursorHeight));
            m_cursorRect.draw(canvas);
        }
    }
}